package com.opensource.svgaplayer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.opensource.svgaplayer.bitmap.SVGABitmapByteArrayDecoder;
import com.opensource.svgaplayer.bitmap.SVGABitmapFileDecoder;
import com.opensource.svgaplayer.entities.SVGAAudioEntity;
import com.opensource.svgaplayer.entities.SVGAVideoSpriteEntity;
import com.opensource.svgaplayer.iml.ParseCompletion;
import com.opensource.svgaplayer.proto.AudioEntity;
import com.opensource.svgaplayer.proto.MovieEntity;
import com.opensource.svgaplayer.proto.MovieParams;
import com.opensource.svgaplayer.proto.SpriteEntity;
import com.opensource.svgaplayer.utils.SVGARect;
import com.opensource.svgaplayer.utils.log.LogUtil;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.audio.AudioManager;
import ohos.media.audio.SoundPlayer;
import ohos.media.image.PixelMap;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONException;
import ohos.utils.zson.ZSONObject;
import okio.ByteString;

/**
 * ex.
 *
 * @author wl
 * @since 2021-06-28
 */
public class SVGAVideoEntity {
    private int soundId;

    /**
     * getFPS
     *
     * @return int
     */
    public int getFPS() {
        return FPS;
    }

    private boolean antiAlias = true;
    private MovieEntity movieItem = null;
    private SVGARect videoSize;
    public int FPS = 20;
    public int frames = 0;
    public List<SVGAVideoSpriteEntity> spriteList = new ArrayList<>();
    public List<SVGAAudioEntity> audioList = new ArrayList<>();
    public SoundPlayer soundPlayer = null;
    public HashMap<String, PixelMap> imageMap = new HashMap<>();
    private File mCacheDir;
    private int mFrameHeight;
    private int mFrameWidth;

    /**
     * SVGAVideoEntity
     *
     * @param json     ZSONObject
     * @param cacheDir File
     */
    public SVGAVideoEntity(ZSONObject json, File cacheDir) {
        this(json, cacheDir, 0, 0);
    }

    /**
     * SVGAVideoEntity
     *
     * @param json        ZSONObject
     * @param cacheDir    File
     * @param frameWidth  int
     * @param frameHeight int
     */
    public SVGAVideoEntity(ZSONObject json, File cacheDir, int frameWidth, int frameHeight) {
        mFrameWidth = frameWidth;
        mFrameHeight = frameHeight;
        mCacheDir = cacheDir;
        ZSONObject movieZSONObject = json.getZSONObject("movie");
        if (movieZSONObject == null) {
            return;
        }
        setupByJson(movieZSONObject);
        try {
            parserImages(json);
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        }
        resetSprites(json);
    }

    private void setupByJson(ZSONObject movieObject) {
        ZSONObject viewBoxObject = movieObject.getZSONObject("viewBox");
        if (viewBoxObject != null) {
            double width = viewBoxObject.getDoubleValue("width");
            double height = viewBoxObject.getDoubleValue("height");
            videoSize = new SVGARect(0.0, 0.0, width, height);
        }
        FPS = 20;
        if (movieObject.getInteger("fps") != null) {
            FPS = movieObject.getIntValue("fps");
        }
        frames = movieObject.getIntValue("frames");
    }

    /**
     * SVGAVideoEntity
     *
     * @param entity   MovieEntity
     * @param cacheDir File
     */
    public SVGAVideoEntity(MovieEntity entity, File cacheDir) {
        this(entity, cacheDir, 0, 0);
    }

    /**
     * SVGAVideoEntity
     *
     * @param entity      MovieEntity
     * @param cacheDir    File
     * @param frameWidth  int
     * @param frameHeight int
     */
    public SVGAVideoEntity(MovieEntity entity, File cacheDir, int frameWidth, int frameHeight) {
        this.mFrameWidth = frameWidth;
        this.mFrameHeight = frameHeight;
        this.mCacheDir = cacheDir;
        this.movieItem = entity;
        if (entity.params != null) {
            setupByMovie(entity.params);
        }
        try {

            parserImages(entity);
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        }
        resetSprites(entity);
    }

    private void setupByMovie(MovieParams movieParams) {
        double width = movieParams.viewBoxWidth;
        double height = movieParams.viewBoxHeight;
        videoSize = new SVGARect(0.0, 0.0, width, height);
        FPS = movieParams.fps;
        frames = movieParams.frames;
    }

    /**
     * prepare
     *
     * @param videoItem          SVGAVideoEntity
     * @param completionCallback ParseCompletion
     */
    public void prepare(SVGAVideoEntity videoItem, ParseCompletion completionCallback) {
        if (movieItem == null) {
            invokeCompleteCallback(videoItem, completionCallback);
            LogUtil.loge("1110000111", "空的");
        } else {

            new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                @Override
                public void run() {
                    setupAudios(videoItem, movieItem, completionCallback);
                }
            });
        }
    }

    private void parserImages(ZSONObject json) {
        ZSONObject imgJson = json.getZSONObject("images");
        if (imgJson == null) {
            return;
        }
        Iterator<String> keys = imgJson.keySet().iterator();
        while (keys.hasNext()) {
            try {
                String imgKey = keys.next();
                String filePath = generateBitmapFilePath(imgJson.get(imgKey).toString(), imgKey);
                if (filePath.isEmpty()) {
                    return;
                }
                String bitmapKey = imgKey.replace(".matte", "");
                PixelMap bitmap = createBitmap(filePath);
                if (bitmap != null) {
                    imageMap.put(bitmapKey, bitmap);
                }
            } catch (ZSONException e) {
                e.printStackTrace();
            }
        }
    }

    private void parserImages(MovieEntity obj) {
        if (obj.images != null && obj.images.entrySet() != null) {
            Set<Map.Entry<String, ByteString>> entries = obj.images.entrySet();
            for (Map.Entry<String, ByteString> it : entries) {
                String imageKey = it.getKey();
                byte[] byteArray = it.getValue().toByteArray();
                if (byteArray.length < 4) {
                    continue;
                }
                byte[] fileTag = new byte[byteArray.length - 4];
                for (int i = 0; i < fileTag.length; i++) {
                    fileTag[i] = byteArray[i];
                }
                if (((int) fileTag[0]) == 73 && ((int) fileTag[1]) == 68 && ((int) fileTag[2]) == 51) {
                    continue;
                }
                String filePath = generateBitmapFilePath(it.getValue().utf8(), it.getKey());
                PixelMap bitmap = createBitmap(byteArray, filePath);
                if (bitmap != null) {
                    imageMap.put(imageKey, bitmap);
                }
            }
        }
    }

    private void resetSprites(ZSONObject json) {
        List<SVGAVideoSpriteEntity> mutableList = new ArrayList();
        ZSONArray sprites = json.getZSONArray("sprites");
        if (sprites != null) {
            for (int i = 0; i < sprites.size(); i++) {
                ZSONObject entryJson = sprites.getZSONObject(i);
                if (entryJson != null) {
                    mutableList.add(new SVGAVideoSpriteEntity(entryJson));
                }
            }
        }
        this.spriteList = mutableList;
    }

    private void resetSprites(MovieEntity entity) {
        List<SpriteEntity> spriteEntityList = entity.sprites;
        List<SVGAVideoSpriteEntity> temp = new ArrayList<>();
        if (spriteEntityList != null) {
            for (int i = 0; i < spriteEntityList.size(); i++) {
                SVGAVideoSpriteEntity svgaVideoSpriteEntity = new SVGAVideoSpriteEntity(spriteEntityList.get(i));
                temp.add(svgaVideoSpriteEntity);
            }
        }
        this.spriteList = temp;
    }

    private String generateBitmapFilePath(String imgName, String imgKey) {
        String path = null;
        try {
            path = mCacheDir.getCanonicalPath() + "/" + imgName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        String path1 = path + ".png";
        String path2 = null;
        try {
            path2 = mCacheDir.getCanonicalPath() + "/" + imgKey + ".png";
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (new File(path).exists()) {
            return path;
        } else if (new File(path1).exists()) {
            return path1;
        } else if (new File(path2).exists()) {
            return path2;
        }
        return "";
    }

    private PixelMap createBitmap(String filePath) {
        return SVGABitmapFileDecoder.getInstance().decodeBitmapFrom(filePath, mFrameWidth, mFrameHeight);
    }

    private PixelMap createBitmap(byte[] byteArray, String filePath) {
        PixelMap bitmap = SVGABitmapByteArrayDecoder.getInstance().decodeBitmapFrom(byteArray, mFrameWidth, mFrameHeight);
        return bitmap != null ? bitmap : createBitmap(filePath);
    }

    /**
     * isAntiAlias
     *
     * @return boolean
     */
    public boolean isAntiAlias() {
        return antiAlias;
    }

    /**
     * setAntiAlias
     *
     * @param antiAlias boolean
     */
    public void setAntiAlias(boolean antiAlias) {
        this.antiAlias = antiAlias;
    }

    /**
     * getMovieItem
     *
     * @return MovieEntity
     */
    public MovieEntity getMovieItem() {
        return movieItem;
    }

    /**
     * setMovieItem
     *
     * @param movieItem MovieEntity
     */
    public void setMovieItem(MovieEntity movieItem) {
        this.movieItem = movieItem;
    }

    private void setupAudios(SVGAVideoEntity videoItem, MovieEntity entity, ParseCompletion completionCallback) {
        if (entity.audios == null || entity.audios.isEmpty()) {
            invokeCompleteCallback(videoItem, completionCallback);
            return;
        }
        soundPlayer = generateSoundPool();
        HashMap<String, File> audiosFileMap = generateAudioFileMap(entity);
        List<SVGAAudioEntity> temp = new ArrayList<>();
        for (AudioEntity audio : entity.audios) {
            SVGAAudioEntity svgaAudioEntity = createSvgaAudioEntity(audio, audiosFileMap);
            temp.add(svgaAudioEntity);
        }
        this.audioList = temp;
        soundPlayer.setOnCreateCompleteListener(new SoundPlayer.OnCreateCompleteListener() {
            int soundLoaded = 0;

            @Override
            public void onCreateComplete(SoundPlayer soundPlayer, int cacheId, int status) {
                soundLoaded++;
                invokeCompleteCallback(videoItem, completionCallback);
               /* if (soundLoaded >= entity.audios.size()) {
                }*/
            }
        });
    }

    private SVGAAudioEntity createSvgaAudioEntity(AudioEntity audio, HashMap<String, File> audiosFileMap) {
        SVGAAudioEntity item = new SVGAAudioEntity(audio);
        double startTime = audio.startTime;
        double totalTime = audio.totalTime;
        if (((int) totalTime) == 0) {
            return item;
        }
        File file = audiosFileMap.get(audio.audioKey);
        if (file != null) {
            FileInputStream it = null;
            try {
                it = new FileInputStream(file);
                double length = Double.valueOf(it.available());
                long offset = (long) ((startTime / totalTime) * length);
                if (soundPlayer != null) {
                    soundId = soundPlayer.createSound(it.getFD(), offset, (long) length);
                    item.setSoundID(soundId);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (it != null) {
                        it.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return item;
    }

    private File generateAudioFile(File audioCache, byte[] value) {
        FileOutputStream fileOutputStream = null;
        try {
            boolean newFile = audioCache.createNewFile();
            if (newFile) {
                fileOutputStream = new FileOutputStream(audioCache);
                fileOutputStream.write(value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return audioCache;
    }

    private HashMap<String, File> generateAudioFileMap(MovieEntity entity) {
        HashMap<String, byte[]> audiosDataMap = generateAudioMap(entity);
        HashMap<String, File> audiosFileMap = new HashMap<>();
        if (audiosDataMap.size() > 0) {
            for (Map.Entry<String, byte[]> it : audiosDataMap.entrySet()) {
                File audioCache = SVGACache.getInstance().buildAudioFile(it.getKey());
                if (audioCache.exists()) {
                    audiosFileMap.put(it.getKey(), audioCache);
                } else {
                    audiosFileMap.put(it.getKey(), generateAudioFile(audioCache, it.getValue()));
                }
            }
        }
        return audiosFileMap;
    }

    private HashMap<String, byte[]> generateAudioMap(MovieEntity entity) {
        HashMap<String, byte[]> audiosDataMap = new HashMap<>();
        if (entity.images != null && entity.images.entrySet() != null) {
            Set<Map.Entry<String, ByteString>> entries = entity.images.entrySet();
            for (Map.Entry<String, ByteString> it : entries) {
                String imageKey = it.getKey();
                byte[] byteArray = it.getValue().toByteArray();
                if (byteArray.length < 4) {
                    continue;
                }
                byte[] fileTag = new byte[byteArray.length - 4];
                for (int i = 0; i < fileTag.length; i++) {
                    fileTag[i] = byteArray[i];
                }
                if (((int) fileTag[0]) == 73 && ((int) fileTag[1]) == 68 && ((int) fileTag[2]) == 51) {
                    audiosDataMap.put(imageKey, byteArray);
                }
            }
        }
        return audiosDataMap;
    }

    private void setupSoundPool(SVGAVideoEntity videoItem, MovieEntity entity, ParseCompletion completionCallback) {
        if (soundPlayer != null) {
            invokeCompleteCallback(videoItem, completionCallback);
            soundPlayer.setOnCreateCompleteListener(new SoundPlayer.OnCreateCompleteListener() {
                int soundLoaded = 0;

                @Override
                public void onCreateComplete(SoundPlayer soundPlayer, int cacheId, int status) {
                    soundLoaded++;
                    invokeCompleteCallback(videoItem, completionCallback);
                    /*if (soundLoaded >= entity.audios.size()) {
                    }*/
                }
            });
        } else {
        }
    }

    private void invokeCompleteCallback(SVGAVideoEntity videoItem, ParseCompletion callback) {
        new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onComplete(videoItem);
                }
            }
        });
    }

    private void invokeErrorCallback(Exception e, ParseCompletion callback) {
        e.printStackTrace();
        LogUtil.loge(e.getMessage());
        new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onError();
                }
            }
        });
    }

    private SoundPlayer generateSoundPool() {
        if (soundPlayer == null) {
            soundPlayer = new SoundPlayer(AudioManager.AudioVolumeType.STREAM_MUSIC.getValue());
        }
        return soundPlayer;
    }

    /**
     * getVideoSize
     *
     * @return SVGARect
     */
    public SVGARect getVideoSize() {
        return videoSize;
    }

    /**
     * clear
     */
    public void clear() {
        if (soundPlayer != null) {
            soundPlayer.release();
        }
        soundPlayer = null;
        if (audioList != null) {
            audioList.clear();
        }
        if (spriteList != null) {
            spriteList.clear();
        }
        if (imageMap != null) {
            imageMap.clear();
        }
    }
}
